1 Data Exploration

a. Violation Code and Fine Amounts

library(tidyverse)
#setwd("C:/Users/ydeng/Desktop/Evelyn/Columbia/Academic Class 2019 01 Data Visualization/Assignments/Assignment 2")

Read in relevant data files.

data <- read_csv("parkingNYC_Jan2019.csv")
codes <- read_csv("parkingNYC_ParkingViolationCodes_Nov_2018.csv")

Clean up the variable names in both files.

data <- subset(data, select= -issue_date)
names(data) <- tolower(names(data))
names(data) <- chartr(old = " ", new = "_", names(data))
names(codes) <- tolower(names(codes))
names(codes) <- chartr(old = " ", new = "_", names(codes))
#get rid of all caps in the descriptions
codes$violation_description <- tolower(codes$violation_description)
write.csv(codes, file = "codes.csv")
data <- subset(data, select= -violation_description)

Add violation code descriptions and fine amounts to the main data file.

data <- data %>%
  left_join(codes, by = "violation_code")

Recode repeated violation descriptions.

data <- mutate(data, violation_description =
              case_when(
                violation_description == "fail to disp. muni meter recpt" ~
                  "fail to dsply muni meter recpt",
                TRUE ~ violation_description
              ))

Display the data by the ten most common violations.

violations_by_frequency <- data %>%
  group_by(violation_description) %>%
  summarise(violation_n = n()) %>%
  arrange(desc(violation_n))
  
violations_by_frequency[1:10,]

Compare how this ranking differs if we focus on the total amount of revenue generated.

For the purpose of differentiating ticket prices, I will group all precincts from 1-24 as receiving the price for below 96th street, and all precincts 25 and above as receiving the price for above 96th street.

#rename the variables for fine amounts
names(data)[47:48] <- c("south_fine_amt", "north_fine_amt")
#mark precincts are north and south
data <- data %>%
  mutate(north_south = case_when(
    violation_precinct >= 25 ~ "north",
    TRUE ~ "south"
  ))

Find the subtotal fine amounts by violation in the north and south, respectively

north_fines <- data %>%
  subset(north_south=="north") %>%
  group_by(violation_description) %>%
  summarize(north_subtotal = sum(north_fine_amt))
south_fines <- data %>%
  subset(north_south=="south" | is.na(north_south)) %>%
  group_by(violation_description) %>%
  summarize(south_subtotal = sum(south_fine_amt))

Find the total fine amount by violation

total_fines <- full_join(north_fines, south_fines, 
                        by = "violation_description")
#replace na values with 0 in order to add them later
total_fines$north_subtotal[is.na(total_fines$north_subtotal)] <- 0
total_fines$south_subtotal[is.na(total_fines$south_subtotal)] <- 0
total_fines$total_revenue <- total_fines$north_subtotal + total_fines$south_subtotal
violations_by_revenue <- total_fines%>%
  arrange(desc(total_revenue)) 
violations_by_revenue$north_subtotal = NULL
violations_by_revenue$south_subtotal = NULL
violations_by_revenue[1:10,]

Compare the total fine amount with the violation frequency

violations_by_revenue$ranking <- c(1:nrow(violations_by_revenue))
violations_by_frequency$ranking <- c(1:nrow(violations_by_frequency))
names(violations_by_revenue)[1] <- "revenue_violations"
names(violations_by_frequency)[1] <- "frequency_violations"
revenue_freq_comparison <- left_join(violations_by_frequency, violations_by_revenue, by = "ranking")
revenue_freq_comparison[1:10,]

It looks like most categories in the top 10 on one list are also in the top 10 on the other list, though specific ranking changes.

b. Average Amount of Fine by Vehicle

Compare the average amount of fine by vehicle color, vehicle year, and vehicle plate type [Hint: it is sufficient to restrict your attention to commercial (COM) and passenger (PAS) vehicles]? Briefly describe your findings.

First, add a new variable that shows how much each violation was actually fined

north_data <- subset(data, north_south=="north")
names(north_data)[48] <- "actual_fine_amt"
north_data$south_fine_amt <- NULL
south_data <- subset(data, north_south=="south")
names(south_data)[47] <- "actual_fine_amt"
south_data$north_fine_amt <- NULL
data <- rbind(north_data, south_data)

Recode/regroup vehicle colors into fewer categories. Some of the codes for colors of vehicles were difficult to decipher and therefore grouped as “other”.

colors <- data$vehicle_color
data$new_colors <- NULL
data$new_colors <- case_when(
     startsWith(colors, "BK") | startsWith (colors, "BLACK") |startsWith (colors,  "BLK")| 
       startsWith(colors, "DK/") ~ "black",
     colors=="B" ~"black",
     startsWith(colors,"BL") | startsWith(colors,"DKB") | startsWith(colors, "LTB")~ "blue",
     startsWith(colors,"BR") | startsWith(colors,"BN") | startsWith(colors,"BW") |
       startsWith(colors,"T") | startsWith(colors,"LTT")  | startsWith(colors,"LT/")~ "brown/tan",
     startsWith(colors,"GREY")|startsWith(colors,"GRA")| startsWith(colors,"GRY") | 
       startsWith(colors,"GY") | startsWith(colors,"S") ~"gray/silver",
     colors=="GR" |colors=="GR/" |colors=="G"~ "gray/silver",
     startsWith(colors,"GRE")| startsWith(colors,"GRN") | startsWith(colors,"DKG") | 
       startsWith(colors,"GN") | startsWith(colors,"LTG")~ "green",
     startsWith(colors,"GL") | startsWith(colors,"GLD") | startsWith(colors,"Y")  | 
       startsWith(colors,"OR") ~ "yellow/orange",
     startsWith(colors,"LAVEN") | startsWith(colors,"DKP") | startsWith(colors,"P")~ "pink/purple",
     startsWith(colors,"LT/") | startsWith(colors,"W") ~ "white",
     startsWith(colors,"R") | startsWith(colors,"M") |startsWith(colors,"DKM") |
       startsWith(colors,"DKR")~ "red/marroon",
     is.na(colors) ~ NA_character_,
     TRUE ~ "other"
   )
head(cbind(data$vehicle_color, data$new_colors), 10)
      [,1]    [,2]         
 [1,] "GRAY"  "gray/silver"
 [2,] "BLK"   "black"      
 [3,] NA      NA           
 [4,] "BLACK" "black"      
 [5,] "BLK"   "black"      
 [6,] "GY/SI" "gray/silver"
 [7,] "BLK"   "black"      
 [8,] "GY"    "gray/silver"
 [9,] "TAN"   "brown/tan"  
[10,] "BLACK" "black"      

Look at average fines by color of the vehicle.

cars_by_color <- data %>%
  group_by(new_colors) %>%
  summarise(avg_fine = mean(actual_fine_amt, na.rm=TRUE)) %>%
  arrange(desc(avg_fine))
cars_by_color

Brown/tan colored vehicles were fined the most heavily, as well as vehicles for which the color was not recorded. After that, it seems that perhaps brightly colored cars that stand out, such as green, yellow, or pink vehicles were fined moderately heavily. Black, white, and gray cars were not heavily fined, and blue cars were least heavily fined. Perhaps blue car owners are seen as more dependable, though more research would be needed to draw these conclusions.

Look at average fines by plate type of the vehicle.

cars_by_plate <- data %>%
  subset(plate_type=="COM" | plate_type=="PAS") %>%
  group_by(plate_type) %>%
  summarise(avg_fine = mean(actual_fine_amt, na.rm=TRUE)) %>%
  arrange(desc(avg_fine))
cars_by_plate

Vehicles with passenger plates have an average fine of $86.49 and those with commercial plates have an average fine of $89.18. Therefore, on average, commercial vehicles seem to be fined slightly more heavily.

Look at average fines by year of the vehicle.

cars_by_year <- data %>%
  subset(vehicle_year<=2020 & vehicle_year > 0) %>%
  group_by(vehicle_year) %>%
  summarise(avg_fine = mean(actual_fine_amt, na.rm=TRUE)) %>%
  arrange(desc(avg_fine))
cars_by_year

Vehicles from the 90’s that are pretty old seem to be fined the most heavily, and very old vehicles from the 70’s (probably specialty/antique ones) are fined the least heavily. For vehicles that were made within the past ten years, the newest vehicles from 2020 and 2019 are fined the most heavily.

2. Map by Precincts

Read in the shape files for the police precincts and remove all precincts outside of Manhattan.

library(rgdal)
nypp <- readOGR(getwd(),"nypp") 
OGR data source with driver: ESRI Shapefile 
Source: "C:\Users\dfz12\Desktop\Assignment 2", layer: "nypp"
with 77 features
It has 3 fields
Manhattan_precincts <- c(1, 5, 6, 7, 9, 10, 13, 14, 17, 18, 19, 20, 22, 23, 24, 25, 26, 28, 30, 32, 33, 34)
nypp <- subset(nypp, Precinct %in% Manhattan_precincts) 
nypp <- spTransform(nypp, CRS("+proj=longlat +datum=WGS84")) 
nypp <- fortify(nypp)

Recode id in the nypp dataset to reflect precinct numbers.

nypp <- nypp %>%
  mutate(precinct = case_when(
    id==0~1, id==1~5, id==2~6, id==3~7, id==4~9, id==5~10,
    id==6~13, id==7~14, id==8~17, id==9~18, id==10~19,
    id==11~20, id==12~22, id==13~23, id==14~24,id==15~25,
    id==16~26, id==17~28, id==18~30, id==19~32, id==20~33,
    id==21~34 ))
nypp$id <- NULL
library(ggmap)
library(mapproj)

Subset violations data to Manhattan only.

Manhattan_violations <- data %>%
  subset(violation_precinct %in% Manhattan_precincts)

a. Number of Tickets, Total Fines, and Average Fines

1. Structure Data

Structure data to get total number of tickets by precinct.

total_tix_df <- Manhattan_violations %>%
  group_by(violation_precinct) %>%
  summarise(n_violations = n()) %>%
  arrange(desc(n_violations))
#add a row for central park, precinct 22, which had no violation records
central_park <- c(22, 1)
#total_tix_df <- rbind(total_tix_df, central_park)

Structure data to get total amount of fines by precinct.

total_fines_df <- Manhattan_violations %>%
  group_by(violation_precinct)%>%
  summarise(total_fines = sum(actual_fine_amt, na.rm=TRUE)) %>%
  arrange(desc(total_fines))
#add a row for central park, precinct 22, which had no violation records
#total_fines_df <- rbind(total_fines_df, central_park)

Structure data to get average cost of fines by precinct.

avg_fines_df <- Manhattan_violations %>%
  group_by(violation_precinct)%>%
  summarise(avg_fine = mean(actual_fine_amt, na.rm=TRUE)) %>%
  arrange(desc(avg_fine))
#add a row for central park, precinct 22, which had no violation records
#avg_fines_df <- rbind(avg_fines_df, central_park)

2. Join Datasets

Join the nypp and restructured Manahattan violations datasets

total_tix_df <- left_join(nypp, total_tix_df, by = c("precinct" = "violation_precinct"))
total_fines_df <- left_join(nypp, total_fines_df, by = c("precinct" = "violation_precinct"))
avg_fines_df <- left_join(nypp, avg_fines_df, by = c("precinct" = "violation_precinct"))
#drop precinct 22 data, since there are no violations in this precinct.
total_tix_df <- total_tix_df[total_tix_df$precinct!=22,]
total_fines_df <- total_fines_df[total_fines_df$precinct!=22,]
avg_fines_df <- avg_fines_df[avg_fines_df$precinct!=22,]

3. Plot Choropleth Maps

Find the centers of each precinct in order to label them in the map.

precinct_labels <- nypp %>%
  group_by(precinct) %>%
  summarise(lat_center = mean(lat), long_center=mean(long))

Get the raster map background.

Manhattan_map <- get_map("Manhattan", source = "stamen",
                         zoom=12, maptype="terrain-background")
g <- ggmap(Manhattan_map)

Plot a choropleth map of the total number of violations in each precinct.

g + 
  geom_polygon(data = total_tix_df, aes(x = long, y = lat, group=group, 
                                        fill =total_tix_df$n_violations), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Number of Total Violations",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center))

Plot a choropleth map of the total amount of fines in each precinct.

g + 
  geom_polygon(data = total_fines_df, aes(x = long, y = lat, group=group, 
                                          fill = total_fines_df$total_fines), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Amount of Total Fines",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center))

Plot a choropleth map of the average amount of fines in each precinct.

g + 
  geom_polygon(data = avg_fines_df, aes(x = long, y = lat, group=group, 
                 fill = avg_fines_df$avg_fine), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Amount of Average Fines",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center))

From these three maps, I see that the maps for total number of tickets and for total revenue looks pretty similar. In these two maps, precinct 19 stands out as the one with both the most number of tickets and yielding the most revenue, followed by precincts 18, 14, and 13. However, precinct 19’s top ranking in these two maps may be due to its large area. Because when looking at the average amount of fines per ticket, precinct 19’s ranking drops down significantly and precincts 18, 17, and 14 have the highest average fines.

b. Types of violations

1. Structure Data

Group the almost 100 types of ticket violations into a smaller set of 4-6 subgroups (where other should be the remainder of violations not included in other groups you defined).

#Here are the groups of violations and their violation codes:
documentation     <- c(29,  82, 1,  74, 83, 72, 71, 75, 2,  73, 70)
improperly_parked <- c(60,  59, 62, 46, 47, 61)
stand_stop        <- c(8,   22, 64, 18, 19, 31, 14, 17, 16, 26, 11, 13, 89, 30, 10)
prohibited_zone   <- c(48, 4, 50, 49, 40, 52,   78, 24, 21, 23, 
                              98,   9, 77, 54, 67, 53, 51, 45, 3)
time_limit        <- c(37,  42, 38, 86, 20, 39, 44, 69)
other             <- c(66, 41,  80, 68, 99, 84, 35, 85, 79)
#Create dummy variables with the different types of violations.
violtn_type_df <- Manhattan_violations %>%
  mutate(violtn_documentation = case_when(
    violation_code %in% documentation ~ 1,
    TRUE ~ 0 )) %>%
  mutate(violtn_improperly_parked = case_when(
    violation_code %in% improperly_parked ~ 1,
    TRUE ~ 0 )) %>%
  mutate(violtn_stand_stop = case_when(
    violation_code %in% stand_stop ~ 1,
    TRUE ~ 0 )) %>%
  mutate(violtn_prohibited_zone = case_when(
    violation_code %in% prohibited_zone ~ 1,
    TRUE ~ 0 )) %>%
  mutate(violtn_time_limit = case_when(
    violation_code %in% time_limit ~ 1,
    TRUE ~ 0 )) %>%
  mutate(violtn_other = case_when(
    violation_code %in% other ~ 1,
    TRUE ~ 0 ))

Count violation types by precinct

violtn_type_df <- violtn_type_df %>%
  group_by(violation_precinct) %>%
  summarise(violtn_documentation= sum(violtn_documentation), 
            violtn_improperly_parked= sum(violtn_improperly_parked), 
            violtn_prohibited_zone= sum(violtn_prohibited_zone), 
            violtn_stand_stop= sum(violtn_stand_stop),
            violtn_time_limit=sum(violtn_time_limit),
            violtn_other=sum(violtn_other))

2 Join Datasets

Join the nypp and violations by type datasets

violtn_type_df <- left_join(nypp, violtn_type_df, by = c("precinct" = "violation_precinct"))
#drop precinct 22 data, since there are no violations in this precinct.
violtn_type_df <- violtn_type_df[violtn_type_df$precinct!=22,]

3 Plot Choropleth Maps

Provide choropleth maps for each of these subgroups to show where different types of violations are more or less common.

Vehicles with Improper Documentation

g + 
  geom_polygon(data = violtn_type_df, aes(x = long, y = lat, group=group, 
                 fill = violtn_documentation), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Number of\ndocumentation\nviolations",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center)) +
  theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(),
        axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank())

Improperly Parked Vehicles

g + 
  geom_polygon(data = violtn_type_df, aes(x = long, y = lat, group=group, 
                 fill = violtn_improperly_parked), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Number of\nimproper parking\nviolations",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center)) +
  theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(),
        axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank())

Vehicles Parked in Prohibited Zones

g + 
  geom_polygon(data = violtn_type_df, aes(x = long, y = lat, group=group, 
                 fill = violtn_prohibited_zone), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Number of\nvehicles parked in\nprohibited zones",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center)) +
  theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(),
        axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank())

Standing and Stopping Violations

g + 
  geom_polygon(data = violtn_type_df, aes(x = long, y = lat, group=group, 
                 fill = violtn_stand_stop), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Number of\nstanding and stopping\nviolations",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center)) +
  theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(),
        axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank())

Time Limit Violations

g + 
  geom_polygon(data = violtn_type_df, aes(x = long, y = lat, group=group, 
                 fill = violtn_time_limit), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Number of\ntime limit\nviolations",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center)) +
  theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(),
        axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank())

Other Violations

g + 
  geom_polygon(data = violtn_type_df, aes(x = long, y = lat, group=group, 
                 fill = violtn_other), 
                 color = "white", size = 0.8) +
  scale_fill_gradient(name="Number of\nuncategorized\nviolations",
                      low = "#fff7f7",high = "#ff0000") +
  geom_text(data = precinct_labels, inherit.aes = FALSE,
            aes(label=precinct, x = long_center, y = lat_center)) +
  theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank(),
        axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank())

3. Focus on the Upper East Side

a. Ignoring fire hydrants

Restrict data to parking violations related to fire hydrants.

Also restrict to violation addresses with numbers as the house number, to avoid issues with geocoding later on, and vehicles with passenger plates, to reduce the amount of data.

Manhattan_df_Q3 <- Manhattan_violations
Manhattan_df_Q3$house_number <- as.numeric(Manhattan_df_Q3$house_number)
UES_df <- Manhattan_df_Q3 %>%
  subset(violation_precinct == 19 & violation_code == 40 & 
           !is.na(house_number) & plate_type == "PAS")

Keep only useful variables in the UES data

UES_df <- cbind(UES_df[1:2], UES_df[8], UES_df[24:25], UES_df[46])

Using the variables “Street Name” and “House Number” as well as the knowledge that these addresses are in the Upper East Side of Manhattan, geocode at least 500 addresses.

# make a new variable "address"
UES_df$address <- paste(UES_df$house_number, UES_df$street_name, ", upper east side, Manhattan NY")
geocodes_df <- read_csv("geocodes_df.csv")
#geocodes_df <- geocode(UES_df$address)

add geocodes to the UES data

UES_df <- cbind(UES_df[1:3], UES_df[6:7], geocodes_df)

Include a data table of these addresses and the latitude and longitude of these addresses in the output.

library(DT)
dt_headers <- c("Summons ID", "Plate ID", "Make", "Violation Type", "Address of Violation", "Longitude", "Latitude")
datatable(UES_df, rownames=FALSE, colnames=dt_headers,
          filter=list(position="top"), options = list(columnDefs = list(list(className = 'dt-left', targets = 0:2))
                                                      ))

b. Interactive Map

library(leaflet)
library(readr)

Subset data so that it excludes any violations geocoded to show up outside of the upper east side (likely incorrectly coded to precinct 19).

UES_df <- UES_df[UES_df$lon > -73.972 & UES_df$lon < -73.945 & UES_df$lat > 40.760 & UES_df$lat < 40.788, ]

Create popup content.

content <- paste("Plate ID:", UES_df$plate_id, "<br/>",
                 "Vehicle Make:", UES_df$vehicle_make, "<br/>",
                 "Address:", UES_df$address)

Provide an interactive map of the violations you geocoded using leaflet. Provide at least three pieces of information on the parking ticket in a popup.

I’m not sure why the addTiles() and addProviderTiles() are not working. It appears that the code is correct but there is an issue perhaps with my coding environment or set up. Eddie and I have a conversation about this on Piazza but this issue was not resolved by the time the assignment was submitted. Therefore, all of my interactive maps in question 3 do not have tiles added.

map3b <- leaflet(UES_df, options = leafletOptions(minZoom = 12)) %>%
  addProviderTiles("Stamen.TonerLite") %>%   
  addCircles(col= "yellow", opacity=1, popup = content,
             highlightOptions = highlightOptions(
               color='#0061ff', weight = 5,
               bringToFront = TRUE, sendToBack = TRUE)) %>%
  setView(lng = -73.956, lat = 40.7738, zoom = 14)
  
map3b

c. Luxury Cars and Repeat Offenders

Using the vehicle Plate ID, identify repeat offenders (in the full dataset).

repeaters_df <- Manhattan_violations %>%
  group_by(plate_id) %>%
  summarise(num_offenses = n())

Create another variable called luxury_car in which you identify luxury car brands using the Vehicle Make variable.

luxury_brands <- c("BMW",   "FIAT", "CHRYS",    "LEXUS",    "CADIL",    "AUDI", "PORSC",    
                   "JAGUA", "CADI", "CHRY", "LINC", "FERRA",    "LAMBO",    "LEXU", 
                   "ZENIT", "ZENTI",    "ROLLS",    "MASE", "BENTL",    "LINCO")
luxury_cars_df <- Manhattan_violations %>%
  mutate(car_type = case_when(
    vehicle_make %in% luxury_brands ~ "Luxury Car",
    TRUE ~ "Non-Luxury Car"
  ))

Merge luxury_cars_df with geocodes and then with repeaters_df, using plate_id

luxury_cars_df <- luxury_cars_df %>%
  left_join(repeaters_df, by = "plate_id") %>%
  mutate(repeater = case_when(               #make binary variable to identify repeaters
    num_offenses > 1 ~ "Yes",
    TRUE ~ "No"
  ))

Keep only necessary variables, then join with UES_df

luxury_cars_df <- luxury_cars_df %>%
  select(summons_number, car_type, repeater)
UES_df_3c <- UES_df %>%
  left_join(luxury_cars_df, by = "summons_number")

set the color scheme for car type

library(RColorBrewer)
palette_3b = colorFactor("Set1", domain = UES_df_3c$car_type) # Grab a palette
color_car_type = palette_3b(UES_df_3c$car_type)

create new popup content

content2 <- paste("Plate ID:", UES_df_3c$plate_id, "<br/>",
                 "Vehicle Make:", UES_df_3c$vehicle_make, "<br/>",
                 "Address:", UES_df_3c$address, "<br/>",
                 "Car Type:", UES_df_3c$car_type, "<br/>",
                 "Repeat Offender:", UES_df_3c$repeater)

Start with the previous map. Distinguish the points by whether the car is a repeat offender and/or luxury car. Add a legend informing the user about the color scheme. Also make sure that the added information about the car type and repeat offender status is now contained in the popup information. Show this map.

#add on to the original map from 3b.
map3c <- leaflet(UES_df_3c, options = leafletOptions(minZoom = 12)) %>%
  addProviderTiles("Stamen.TonerLite") %>%   
  addCircles(color = color_car_type, opacity=1, popup = content2,
             highlightOptions = highlightOptions(
               color='#0061ff', weight = 5,
               bringToFront = TRUE, sendToBack = TRUE)) %>%
  addLegend(pal = palette_3b, values = ~UES_df_3c$car_type, title = "Car Type") %>%
  setView(lng = -73.956, lat = 40.7738, zoom = 14)
map3c

d. Cluster

Add marker clustering, so that zooming in will reveal the individual locations but the zoomed out map only shows the clusters. Show the map with clusters.

map3d <- leaflet(UES_df_3c, options = leafletOptions(minZoom = 12)) %>%
  addProviderTiles("Stamen.TonerLite") %>%   
  addCircleMarkers(color = color_car_type, 
                       popup = content2,
                       clusterOptions = markerClusterOptions()) %>%
  addLegend(pal = palette_3b, values = ~UES_df_3c$car_type, title = "Car Type") %>%
  setView(lng = -73.956, lat = 40.7738, zoom = 14) 
map3d
LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiAtIEFzc2lnbm1lbnQgMiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0b2M6IHllcw0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpDQpgYGANCg0KIzEgRGF0YSBFeHBsb3JhdGlvbg0KIyNhLiBWaW9sYXRpb24gQ29kZSBhbmQgRmluZSBBbW91bnRzDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQojc2V0d2QoIkM6L1VzZXJzL3lkZW5nL0Rlc2t0b3AvRXZlbHluL0NvbHVtYmlhL0FjYWRlbWljIENsYXNzIDIwMTkgMDEgRGF0YSBWaXN1YWxpemF0aW9uL0Fzc2lnbm1lbnRzL0Fzc2lnbm1lbnQgMiIpDQpgYGANCg0KKlJlYWQgaW4gcmVsZXZhbnQgZGF0YSBmaWxlcy4qDQpgYGB7cn0NCmRhdGEgPC0gcmVhZF9jc3YoInBhcmtpbmdOWUNfSmFuMjAxOS5jc3YiKQ0KY29kZXMgPC0gcmVhZF9jc3YoInBhcmtpbmdOWUNfUGFya2luZ1Zpb2xhdGlvbkNvZGVzX05vdl8yMDE4LmNzdiIpDQpgYGANCg0KKkNsZWFuIHVwIHRoZSB2YXJpYWJsZSBuYW1lcyBpbiBib3RoIGZpbGVzLioNCmBgYHtyfQ0KZGF0YSA8LSBzdWJzZXQoZGF0YSwgc2VsZWN0PSAtaXNzdWVfZGF0ZSkNCg0KbmFtZXMoZGF0YSkgPC0gdG9sb3dlcihuYW1lcyhkYXRhKSkNCm5hbWVzKGRhdGEpIDwtIGNoYXJ0cihvbGQgPSAiICIsIG5ldyA9ICJfIiwgbmFtZXMoZGF0YSkpDQoNCm5hbWVzKGNvZGVzKSA8LSB0b2xvd2VyKG5hbWVzKGNvZGVzKSkNCm5hbWVzKGNvZGVzKSA8LSBjaGFydHIob2xkID0gIiAiLCBuZXcgPSAiXyIsIG5hbWVzKGNvZGVzKSkNCg0KI2dldCByaWQgb2YgYWxsIGNhcHMgaW4gdGhlIGRlc2NyaXB0aW9ucw0KY29kZXMkdmlvbGF0aW9uX2Rlc2NyaXB0aW9uIDwtIHRvbG93ZXIoY29kZXMkdmlvbGF0aW9uX2Rlc2NyaXB0aW9uKQ0Kd3JpdGUuY3N2KGNvZGVzLCBmaWxlID0gImNvZGVzLmNzdiIpDQoNCmRhdGEgPC0gc3Vic2V0KGRhdGEsIHNlbGVjdD0gLXZpb2xhdGlvbl9kZXNjcmlwdGlvbikNCmBgYA0KDQoqQWRkIHZpb2xhdGlvbiBjb2RlIGRlc2NyaXB0aW9ucyBhbmQgZmluZSBhbW91bnRzIHRvIHRoZSBtYWluIGRhdGEgZmlsZS4qDQpgYGB7cn0NCmRhdGEgPC0gZGF0YSAlPiUNCiAgbGVmdF9qb2luKGNvZGVzLCBieSA9ICJ2aW9sYXRpb25fY29kZSIpDQpgYGANCg0KKlJlY29kZSByZXBlYXRlZCB2aW9sYXRpb24gZGVzY3JpcHRpb25zLioNCmBgYHtyfQ0KZGF0YSA8LSBtdXRhdGUoZGF0YSwgdmlvbGF0aW9uX2Rlc2NyaXB0aW9uID0NCiAgICAgICAgICAgICAgY2FzZV93aGVuKA0KICAgICAgICAgICAgICAgIHZpb2xhdGlvbl9kZXNjcmlwdGlvbiA9PSAiZmFpbCB0byBkaXNwLiBtdW5pIG1ldGVyIHJlY3B0IiB+DQogICAgICAgICAgICAgICAgICAiZmFpbCB0byBkc3BseSBtdW5pIG1ldGVyIHJlY3B0IiwNCiAgICAgICAgICAgICAgICBUUlVFIH4gdmlvbGF0aW9uX2Rlc2NyaXB0aW9uDQogICAgICAgICAgICAgICkpDQpgYGANCg0KKkRpc3BsYXkgdGhlIGRhdGEgYnkgdGhlIHRlbiBtb3N0IGNvbW1vbiB2aW9sYXRpb25zLioNCmBgYHtyfQ0KdmlvbGF0aW9uc19ieV9mcmVxdWVuY3kgPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkodmlvbGF0aW9uX2Rlc2NyaXB0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKHZpb2xhdGlvbl9uID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHZpb2xhdGlvbl9uKSkNCiAgDQp2aW9sYXRpb25zX2J5X2ZyZXF1ZW5jeVsxOjEwLF0NCmBgYA0KDQoqQ29tcGFyZSBob3cgdGhpcyByYW5raW5nIGRpZmZlcnMgaWYgd2UgZm9jdXMgb24gdGhlIHRvdGFsIGFtb3VudCBvZiByZXZlbnVlIGdlbmVyYXRlZC4qDQoNCkZvciB0aGUgcHVycG9zZSBvZiBkaWZmZXJlbnRpYXRpbmcgdGlja2V0IHByaWNlcywgSSB3aWxsIGdyb3VwIGFsbCBwcmVjaW5jdHMgZnJvbSAxLTI0IGFzIHJlY2VpdmluZyB0aGUgcHJpY2UgZm9yIGJlbG93IDk2dGggc3RyZWV0LCBhbmQgYWxsIHByZWNpbmN0cyAyNSBhbmQgYWJvdmUgYXMgcmVjZWl2aW5nIHRoZSBwcmljZSBmb3IgYWJvdmUgOTZ0aCBzdHJlZXQuDQoNCmBgYHtyfQ0KI3JlbmFtZSB0aGUgdmFyaWFibGVzIGZvciBmaW5lIGFtb3VudHMNCm5hbWVzKGRhdGEpWzQ3OjQ4XSA8LSBjKCJzb3V0aF9maW5lX2FtdCIsICJub3J0aF9maW5lX2FtdCIpDQoNCiNtYXJrIHByZWNpbmN0cyBhcmUgbm9ydGggYW5kIHNvdXRoDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZShub3J0aF9zb3V0aCA9IGNhc2Vfd2hlbigNCiAgICB2aW9sYXRpb25fcHJlY2luY3QgPj0gMjUgfiAibm9ydGgiLA0KICAgIFRSVUUgfiAic291dGgiDQogICkpDQpgYGANCg0KRmluZCB0aGUgc3VidG90YWwgZmluZSBhbW91bnRzIGJ5IHZpb2xhdGlvbiBpbiB0aGUgbm9ydGggYW5kIHNvdXRoLCByZXNwZWN0aXZlbHkNCmBgYHtyfQ0Kbm9ydGhfZmluZXMgPC0gZGF0YSAlPiUNCiAgc3Vic2V0KG5vcnRoX3NvdXRoPT0ibm9ydGgiKSAlPiUNCiAgZ3JvdXBfYnkodmlvbGF0aW9uX2Rlc2NyaXB0aW9uKSAlPiUNCiAgc3VtbWFyaXplKG5vcnRoX3N1YnRvdGFsID0gc3VtKG5vcnRoX2ZpbmVfYW10KSkNCg0Kc291dGhfZmluZXMgPC0gZGF0YSAlPiUNCiAgc3Vic2V0KG5vcnRoX3NvdXRoPT0ic291dGgiIHwgaXMubmEobm9ydGhfc291dGgpKSAlPiUNCiAgZ3JvdXBfYnkodmlvbGF0aW9uX2Rlc2NyaXB0aW9uKSAlPiUNCiAgc3VtbWFyaXplKHNvdXRoX3N1YnRvdGFsID0gc3VtKHNvdXRoX2ZpbmVfYW10KSkNCmBgYA0KDQpGaW5kIHRoZSB0b3RhbCBmaW5lIGFtb3VudCBieSB2aW9sYXRpb24NCmBgYHtyfQ0KdG90YWxfZmluZXMgPC0gZnVsbF9qb2luKG5vcnRoX2ZpbmVzLCBzb3V0aF9maW5lcywgDQogICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJ2aW9sYXRpb25fZGVzY3JpcHRpb24iKQ0KDQojcmVwbGFjZSBuYSB2YWx1ZXMgd2l0aCAwIGluIG9yZGVyIHRvIGFkZCB0aGVtIGxhdGVyDQp0b3RhbF9maW5lcyRub3J0aF9zdWJ0b3RhbFtpcy5uYSh0b3RhbF9maW5lcyRub3J0aF9zdWJ0b3RhbCldIDwtIDANCnRvdGFsX2ZpbmVzJHNvdXRoX3N1YnRvdGFsW2lzLm5hKHRvdGFsX2ZpbmVzJHNvdXRoX3N1YnRvdGFsKV0gPC0gMA0KDQp0b3RhbF9maW5lcyR0b3RhbF9yZXZlbnVlIDwtIHRvdGFsX2ZpbmVzJG5vcnRoX3N1YnRvdGFsICsgdG90YWxfZmluZXMkc291dGhfc3VidG90YWwNCg0KdmlvbGF0aW9uc19ieV9yZXZlbnVlIDwtIHRvdGFsX2ZpbmVzJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbF9yZXZlbnVlKSkgDQoNCnZpb2xhdGlvbnNfYnlfcmV2ZW51ZSRub3J0aF9zdWJ0b3RhbCA9IE5VTEwNCnZpb2xhdGlvbnNfYnlfcmV2ZW51ZSRzb3V0aF9zdWJ0b3RhbCA9IE5VTEwNCg0KdmlvbGF0aW9uc19ieV9yZXZlbnVlWzE6MTAsXQ0KYGBgDQoNCg0KQ29tcGFyZSB0aGUgdG90YWwgZmluZSBhbW91bnQgd2l0aCB0aGUgdmlvbGF0aW9uIGZyZXF1ZW5jeQ0KYGBge3J9DQp2aW9sYXRpb25zX2J5X3JldmVudWUkcmFua2luZyA8LSBjKDE6bnJvdyh2aW9sYXRpb25zX2J5X3JldmVudWUpKQ0KdmlvbGF0aW9uc19ieV9mcmVxdWVuY3kkcmFua2luZyA8LSBjKDE6bnJvdyh2aW9sYXRpb25zX2J5X2ZyZXF1ZW5jeSkpDQpuYW1lcyh2aW9sYXRpb25zX2J5X3JldmVudWUpWzFdIDwtICJyZXZlbnVlX3Zpb2xhdGlvbnMiDQpuYW1lcyh2aW9sYXRpb25zX2J5X2ZyZXF1ZW5jeSlbMV0gPC0gImZyZXF1ZW5jeV92aW9sYXRpb25zIg0KDQpyZXZlbnVlX2ZyZXFfY29tcGFyaXNvbiA8LSBsZWZ0X2pvaW4odmlvbGF0aW9uc19ieV9mcmVxdWVuY3ksIHZpb2xhdGlvbnNfYnlfcmV2ZW51ZSwgYnkgPSAicmFua2luZyIpDQoNCnJldmVudWVfZnJlcV9jb21wYXJpc29uWzE6MTAsXQ0KDQpgYGANCkl0IGxvb2tzIGxpa2UgbW9zdCBjYXRlZ29yaWVzIGluIHRoZSB0b3AgMTAgb24gb25lIGxpc3QgYXJlIGFsc28gaW4gdGhlIHRvcCAxMCBvbiB0aGUgb3RoZXIgbGlzdCwgdGhvdWdoIHNwZWNpZmljIHJhbmtpbmcgY2hhbmdlcy4NCg0KIyNiLiBBdmVyYWdlIEFtb3VudCBvZiBGaW5lIGJ5IFZlaGljbGUNCipDb21wYXJlIHRoZSBhdmVyYWdlIGFtb3VudCBvZiBmaW5lIGJ5IHZlaGljbGUgY29sb3IsIHZlaGljbGUgeWVhciwgYW5kIHZlaGljbGUgcGxhdGUgdHlwZSBbSGludDogaXQgaXMgc3VmZmljaWVudCB0byByZXN0cmljdCB5b3VyIGF0dGVudGlvbiB0byBjb21tZXJjaWFsIChDT00pIGFuZCBwYXNzZW5nZXIgKFBBUykgdmVoaWNsZXNdPyBCcmllZmx5IGRlc2NyaWJlIHlvdXIgZmluZGluZ3MuKg0KDQpGaXJzdCwgYWRkIGEgbmV3IHZhcmlhYmxlIHRoYXQgc2hvd3MgaG93IG11Y2ggZWFjaCB2aW9sYXRpb24gd2FzIGFjdHVhbGx5IGZpbmVkDQpgYGB7cn0NCm5vcnRoX2RhdGEgPC0gc3Vic2V0KGRhdGEsIG5vcnRoX3NvdXRoPT0ibm9ydGgiKQ0KbmFtZXMobm9ydGhfZGF0YSlbNDhdIDwtICJhY3R1YWxfZmluZV9hbXQiDQpub3J0aF9kYXRhJHNvdXRoX2ZpbmVfYW10IDwtIE5VTEwNCg0Kc291dGhfZGF0YSA8LSBzdWJzZXQoZGF0YSwgbm9ydGhfc291dGg9PSJzb3V0aCIpDQpuYW1lcyhzb3V0aF9kYXRhKVs0N10gPC0gImFjdHVhbF9maW5lX2FtdCINCnNvdXRoX2RhdGEkbm9ydGhfZmluZV9hbXQgPC0gTlVMTA0KDQpkYXRhIDwtIHJiaW5kKG5vcnRoX2RhdGEsIHNvdXRoX2RhdGEpDQpgYGANCg0KUmVjb2RlL3JlZ3JvdXAgdmVoaWNsZSBjb2xvcnMgaW50byBmZXdlciBjYXRlZ29yaWVzLg0KU29tZSBvZiB0aGUgY29kZXMgZm9yIGNvbG9ycyBvZiB2ZWhpY2xlcyB3ZXJlIGRpZmZpY3VsdCB0byBkZWNpcGhlciBhbmQgdGhlcmVmb3JlIGdyb3VwZWQgYXMgIm90aGVyIi4NCmBgYHtyfQ0KY29sb3JzIDwtIGRhdGEkdmVoaWNsZV9jb2xvcg0KZGF0YSRuZXdfY29sb3JzIDwtIE5VTEwNCmRhdGEkbmV3X2NvbG9ycyA8LSBjYXNlX3doZW4oDQogICAgIHN0YXJ0c1dpdGgoY29sb3JzLCAiQksiKSB8IHN0YXJ0c1dpdGggKGNvbG9ycywgIkJMQUNLIikgfHN0YXJ0c1dpdGggKGNvbG9ycywgICJCTEsiKXwgDQogICAgICAgc3RhcnRzV2l0aChjb2xvcnMsICJESy8iKSB+ICJibGFjayIsDQogICAgIGNvbG9ycz09IkIiIH4iYmxhY2siLA0KICAgICBzdGFydHNXaXRoKGNvbG9ycywiQkwiKSB8IHN0YXJ0c1dpdGgoY29sb3JzLCJES0IiKSB8IHN0YXJ0c1dpdGgoY29sb3JzLCAiTFRCIil+ICJibHVlIiwNCiAgICAgc3RhcnRzV2l0aChjb2xvcnMsIkJSIikgfCBzdGFydHNXaXRoKGNvbG9ycywiQk4iKSB8IHN0YXJ0c1dpdGgoY29sb3JzLCJCVyIpIHwNCiAgICAgICBzdGFydHNXaXRoKGNvbG9ycywiVCIpIHwgc3RhcnRzV2l0aChjb2xvcnMsIkxUVCIpICB8IHN0YXJ0c1dpdGgoY29sb3JzLCJMVC8iKX4gImJyb3duL3RhbiIsDQogICAgIHN0YXJ0c1dpdGgoY29sb3JzLCJHUkVZIil8c3RhcnRzV2l0aChjb2xvcnMsIkdSQSIpfCBzdGFydHNXaXRoKGNvbG9ycywiR1JZIikgfCANCiAgICAgICBzdGFydHNXaXRoKGNvbG9ycywiR1kiKSB8IHN0YXJ0c1dpdGgoY29sb3JzLCJTIikgfiJncmF5L3NpbHZlciIsDQogICAgIGNvbG9ycz09IkdSIiB8Y29sb3JzPT0iR1IvIiB8Y29sb3JzPT0iRyJ+ICJncmF5L3NpbHZlciIsDQogICAgIHN0YXJ0c1dpdGgoY29sb3JzLCJHUkUiKXwgc3RhcnRzV2l0aChjb2xvcnMsIkdSTiIpIHwgc3RhcnRzV2l0aChjb2xvcnMsIkRLRyIpIHwgDQogICAgICAgc3RhcnRzV2l0aChjb2xvcnMsIkdOIikgfCBzdGFydHNXaXRoKGNvbG9ycywiTFRHIil+ICJncmVlbiIsDQogICAgIHN0YXJ0c1dpdGgoY29sb3JzLCJHTCIpIHwgc3RhcnRzV2l0aChjb2xvcnMsIkdMRCIpIHwgc3RhcnRzV2l0aChjb2xvcnMsIlkiKSAgfCANCiAgICAgICBzdGFydHNXaXRoKGNvbG9ycywiT1IiKSB+ICJ5ZWxsb3cvb3JhbmdlIiwNCiAgICAgc3RhcnRzV2l0aChjb2xvcnMsIkxBVkVOIikgfCBzdGFydHNXaXRoKGNvbG9ycywiREtQIikgfCBzdGFydHNXaXRoKGNvbG9ycywiUCIpfiAicGluay9wdXJwbGUiLA0KICAgICBzdGFydHNXaXRoKGNvbG9ycywiTFQvIikgfCBzdGFydHNXaXRoKGNvbG9ycywiVyIpIH4gIndoaXRlIiwNCiAgICAgc3RhcnRzV2l0aChjb2xvcnMsIlIiKSB8IHN0YXJ0c1dpdGgoY29sb3JzLCJNIikgfHN0YXJ0c1dpdGgoY29sb3JzLCJES00iKSB8DQogICAgICAgc3RhcnRzV2l0aChjb2xvcnMsIkRLUiIpfiAicmVkL21hcnJvb24iLA0KICAgICBpcy5uYShjb2xvcnMpIH4gTkFfY2hhcmFjdGVyXywNCiAgICAgVFJVRSB+ICJvdGhlciINCiAgICkNCg0KaGVhZChjYmluZChkYXRhJHZlaGljbGVfY29sb3IsIGRhdGEkbmV3X2NvbG9ycyksIDEwKQ0KYGBgDQoNCioqTG9vayBhdCBhdmVyYWdlIGZpbmVzIGJ5IGNvbG9yIG9mIHRoZSB2ZWhpY2xlLioqDQpgYGB7cn0NCmNhcnNfYnlfY29sb3IgPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkobmV3X2NvbG9ycykgJT4lDQogIHN1bW1hcmlzZShhdmdfZmluZSA9IG1lYW4oYWN0dWFsX2ZpbmVfYW10LCBuYS5ybT1UUlVFKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhhdmdfZmluZSkpDQpjYXJzX2J5X2NvbG9yDQpgYGANCkJyb3duL3RhbiBjb2xvcmVkIHZlaGljbGVzIHdlcmUgZmluZWQgdGhlIG1vc3QgaGVhdmlseSwgYXMgd2VsbCBhcyB2ZWhpY2xlcyBmb3Igd2hpY2ggdGhlIGNvbG9yIHdhcyBub3QgcmVjb3JkZWQuIEFmdGVyIHRoYXQsIGl0IHNlZW1zIHRoYXQgcGVyaGFwcyBicmlnaHRseSBjb2xvcmVkIGNhcnMgdGhhdCBzdGFuZCBvdXQsIHN1Y2ggYXMgZ3JlZW4sIHllbGxvdywgb3IgcGluayB2ZWhpY2xlcyB3ZXJlIGZpbmVkIG1vZGVyYXRlbHkgaGVhdmlseS4gQmxhY2ssIHdoaXRlLCBhbmQgZ3JheSBjYXJzIHdlcmUgbm90IGhlYXZpbHkgZmluZWQsIGFuZCBibHVlIGNhcnMgd2VyZSBsZWFzdCBoZWF2aWx5IGZpbmVkLiBQZXJoYXBzIGJsdWUgY2FyIG93bmVycyBhcmUgc2VlbiBhcyBtb3JlIGRlcGVuZGFibGUsIHRob3VnaCBtb3JlIHJlc2VhcmNoIHdvdWxkIGJlIG5lZWRlZCB0byBkcmF3IHRoZXNlIGNvbmNsdXNpb25zLg0KDQoqKkxvb2sgYXQgYXZlcmFnZSBmaW5lcyBieSBwbGF0ZSB0eXBlIG9mIHRoZSB2ZWhpY2xlLioqDQpgYGB7cn0NCmNhcnNfYnlfcGxhdGUgPC0gZGF0YSAlPiUNCiAgc3Vic2V0KHBsYXRlX3R5cGU9PSJDT00iIHwgcGxhdGVfdHlwZT09IlBBUyIpICU+JQ0KICBncm91cF9ieShwbGF0ZV90eXBlKSAlPiUNCiAgc3VtbWFyaXNlKGF2Z19maW5lID0gbWVhbihhY3R1YWxfZmluZV9hbXQsIG5hLnJtPVRSVUUpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGF2Z19maW5lKSkNCmNhcnNfYnlfcGxhdGUNCg0KYGBgDQpWZWhpY2xlcyB3aXRoIHBhc3NlbmdlciBwbGF0ZXMgaGF2ZSBhbiBhdmVyYWdlIGZpbmUgb2YgXCQ4Ni40OSBhbmQgdGhvc2Ugd2l0aCBjb21tZXJjaWFsIHBsYXRlcyBoYXZlIGFuIGF2ZXJhZ2UgZmluZSBvZiBcJDg5LjE4LiBUaGVyZWZvcmUsIG9uIGF2ZXJhZ2UsIGNvbW1lcmNpYWwgdmVoaWNsZXMgc2VlbSB0byBiZSBmaW5lZCBzbGlnaHRseSBtb3JlIGhlYXZpbHkuDQoNCioqTG9vayBhdCBhdmVyYWdlIGZpbmVzIGJ5IHllYXIgb2YgdGhlIHZlaGljbGUuKioNCmBgYHtyfQ0KY2Fyc19ieV95ZWFyIDwtIGRhdGEgJT4lDQogIHN1YnNldCh2ZWhpY2xlX3llYXI8PTIwMjAgJiB2ZWhpY2xlX3llYXIgPiAwKSAlPiUNCiAgZ3JvdXBfYnkodmVoaWNsZV95ZWFyKSAlPiUNCiAgc3VtbWFyaXNlKGF2Z19maW5lID0gbWVhbihhY3R1YWxfZmluZV9hbXQsIG5hLnJtPVRSVUUpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGF2Z19maW5lKSkNCmNhcnNfYnlfeWVhcg0KDQpgYGANClZlaGljbGVzIGZyb20gdGhlIDkwJ3MgdGhhdCBhcmUgcHJldHR5IG9sZCBzZWVtIHRvIGJlIGZpbmVkIHRoZSBtb3N0IGhlYXZpbHksIGFuZCB2ZXJ5IG9sZCB2ZWhpY2xlcyBmcm9tIHRoZSA3MCdzIChwcm9iYWJseSBzcGVjaWFsdHkvYW50aXF1ZSBvbmVzKSBhcmUgZmluZWQgdGhlIGxlYXN0IGhlYXZpbHkuIEZvciB2ZWhpY2xlcyB0aGF0IHdlcmUgbWFkZSB3aXRoaW4gdGhlIHBhc3QgdGVuIHllYXJzLCB0aGUgbmV3ZXN0IHZlaGljbGVzIGZyb20gMjAyMCBhbmQgMjAxOSBhcmUgZmluZWQgdGhlIG1vc3QgaGVhdmlseS4NCg0KDQojMi4gTWFwIGJ5IFByZWNpbmN0cw0KDQoqUmVhZCBpbiB0aGUgc2hhcGUgZmlsZXMgZm9yIHRoZSBwb2xpY2UgcHJlY2luY3RzIGFuZCByZW1vdmUgYWxsIHByZWNpbmN0cyBvdXRzaWRlIG9mIE1hbmhhdHRhbi4qDQpgYGB7cn0NCmxpYnJhcnkocmdkYWwpDQpueXBwIDwtIHJlYWRPR1IoZ2V0d2QoKSwibnlwcCIpIA0KTWFuaGF0dGFuX3ByZWNpbmN0cyA8LSBjKDEsIDUsIDYsIDcsIDksIDEwLCAxMywgMTQsIDE3LCAxOCwgMTksIDIwLCAyMiwgMjMsIDI0LCAyNSwgMjYsIDI4LCAzMCwgMzIsIDMzLCAzNCkNCm55cHAgPC0gc3Vic2V0KG55cHAsIFByZWNpbmN0ICVpbiUgTWFuaGF0dGFuX3ByZWNpbmN0cykgDQpueXBwIDwtIHNwVHJhbnNmb3JtKG55cHAsIENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiKSkgDQpueXBwIDwtIGZvcnRpZnkobnlwcCkNCmBgYA0KDQpSZWNvZGUgaWQgaW4gdGhlIG55cHAgZGF0YXNldCB0byByZWZsZWN0IHByZWNpbmN0IG51bWJlcnMuDQpgYGB7cn0NCm55cHAgPC0gbnlwcCAlPiUNCiAgbXV0YXRlKHByZWNpbmN0ID0gY2FzZV93aGVuKA0KICAgIGlkPT0wfjEsIGlkPT0xfjUsIGlkPT0yfjYsIGlkPT0zfjcsIGlkPT00fjksIGlkPT01fjEwLA0KICAgIGlkPT02fjEzLCBpZD09N34xNCwgaWQ9PTh+MTcsIGlkPT05fjE4LCBpZD09MTB+MTksDQogICAgaWQ9PTExfjIwLCBpZD09MTJ+MjIsIGlkPT0xM34yMywgaWQ9PTE0fjI0LGlkPT0xNX4yNSwNCiAgICBpZD09MTZ+MjYsIGlkPT0xN34yOCwgaWQ9PTE4fjMwLCBpZD09MTl+MzIsIGlkPT0yMH4zMywNCiAgICBpZD09MjF+MzQgKSkNCm55cHAkaWQgPC0gTlVMTA0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdnbWFwKQ0KbGlicmFyeShtYXBwcm9qKQ0KYGBgDQoNClN1YnNldCB2aW9sYXRpb25zIGRhdGEgdG8gTWFuaGF0dGFuIG9ubHkuDQpgYGB7cn0NCk1hbmhhdHRhbl92aW9sYXRpb25zIDwtIGRhdGEgJT4lDQogIHN1YnNldCh2aW9sYXRpb25fcHJlY2luY3QgJWluJSBNYW5oYXR0YW5fcHJlY2luY3RzKQ0KYGBgDQoNCiMjYS4gTnVtYmVyIG9mIFRpY2tldHMsIFRvdGFsIEZpbmVzLCBhbmQgQXZlcmFnZSBGaW5lcw0KDQojIyMxLiBTdHJ1Y3R1cmUgRGF0YQ0KDQpTdHJ1Y3R1cmUgZGF0YSB0byBnZXQgdG90YWwgbnVtYmVyIG9mIHRpY2tldHMgYnkgcHJlY2luY3QuDQpgYGB7cn0NCnRvdGFsX3RpeF9kZiA8LSBNYW5oYXR0YW5fdmlvbGF0aW9ucyAlPiUNCiAgZ3JvdXBfYnkodmlvbGF0aW9uX3ByZWNpbmN0KSAlPiUNCiAgc3VtbWFyaXNlKG5fdmlvbGF0aW9ucyA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhuX3Zpb2xhdGlvbnMpKQ0KDQojYWRkIGEgcm93IGZvciBjZW50cmFsIHBhcmssIHByZWNpbmN0IDIyLCB3aGljaCBoYWQgbm8gdmlvbGF0aW9uIHJlY29yZHMNCmNlbnRyYWxfcGFyayA8LSBjKDIyLCAxKQ0KI3RvdGFsX3RpeF9kZiA8LSByYmluZCh0b3RhbF90aXhfZGYsIGNlbnRyYWxfcGFyaykNCmBgYA0KDQpTdHJ1Y3R1cmUgZGF0YSB0byBnZXQgdG90YWwgYW1vdW50IG9mIGZpbmVzIGJ5IHByZWNpbmN0Lg0KYGBge3J9DQp0b3RhbF9maW5lc19kZiA8LSBNYW5oYXR0YW5fdmlvbGF0aW9ucyAlPiUNCiAgZ3JvdXBfYnkodmlvbGF0aW9uX3ByZWNpbmN0KSU+JQ0KICBzdW1tYXJpc2UodG90YWxfZmluZXMgPSBzdW0oYWN0dWFsX2ZpbmVfYW10LCBuYS5ybT1UUlVFKSkgJT4lDQogIGFycmFuZ2UoZGVzYyh0b3RhbF9maW5lcykpDQoNCiNhZGQgYSByb3cgZm9yIGNlbnRyYWwgcGFyaywgcHJlY2luY3QgMjIsIHdoaWNoIGhhZCBubyB2aW9sYXRpb24gcmVjb3Jkcw0KI3RvdGFsX2ZpbmVzX2RmIDwtIHJiaW5kKHRvdGFsX2ZpbmVzX2RmLCBjZW50cmFsX3BhcmspDQpgYGANCg0KU3RydWN0dXJlIGRhdGEgdG8gZ2V0IGF2ZXJhZ2UgY29zdCBvZiBmaW5lcyBieSBwcmVjaW5jdC4NCmBgYHtyfQ0KYXZnX2ZpbmVzX2RmIDwtIE1hbmhhdHRhbl92aW9sYXRpb25zICU+JQ0KICBncm91cF9ieSh2aW9sYXRpb25fcHJlY2luY3QpJT4lDQogIHN1bW1hcmlzZShhdmdfZmluZSA9IG1lYW4oYWN0dWFsX2ZpbmVfYW10LCBuYS5ybT1UUlVFKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhhdmdfZmluZSkpDQoNCiNhZGQgYSByb3cgZm9yIGNlbnRyYWwgcGFyaywgcHJlY2luY3QgMjIsIHdoaWNoIGhhZCBubyB2aW9sYXRpb24gcmVjb3Jkcw0KI2F2Z19maW5lc19kZiA8LSByYmluZChhdmdfZmluZXNfZGYsIGNlbnRyYWxfcGFyaykNCmBgYA0KDQojIyMyLiBKb2luIERhdGFzZXRzDQoNCkpvaW4gdGhlIG55cHAgYW5kIHJlc3RydWN0dXJlZCBNYW5haGF0dGFuIHZpb2xhdGlvbnMgZGF0YXNldHMNCmBgYHtyfQ0KdG90YWxfdGl4X2RmIDwtIGxlZnRfam9pbihueXBwLCB0b3RhbF90aXhfZGYsIGJ5ID0gYygicHJlY2luY3QiID0gInZpb2xhdGlvbl9wcmVjaW5jdCIpKQ0KdG90YWxfZmluZXNfZGYgPC0gbGVmdF9qb2luKG55cHAsIHRvdGFsX2ZpbmVzX2RmLCBieSA9IGMoInByZWNpbmN0IiA9ICJ2aW9sYXRpb25fcHJlY2luY3QiKSkNCmF2Z19maW5lc19kZiA8LSBsZWZ0X2pvaW4obnlwcCwgYXZnX2ZpbmVzX2RmLCBieSA9IGMoInByZWNpbmN0IiA9ICJ2aW9sYXRpb25fcHJlY2luY3QiKSkNCg0KI2Ryb3AgcHJlY2luY3QgMjIgZGF0YSwgc2luY2UgdGhlcmUgYXJlIG5vIHZpb2xhdGlvbnMgaW4gdGhpcyBwcmVjaW5jdC4NCnRvdGFsX3RpeF9kZiA8LSB0b3RhbF90aXhfZGZbdG90YWxfdGl4X2RmJHByZWNpbmN0IT0yMixdDQp0b3RhbF9maW5lc19kZiA8LSB0b3RhbF9maW5lc19kZlt0b3RhbF9maW5lc19kZiRwcmVjaW5jdCE9MjIsXQ0KYXZnX2ZpbmVzX2RmIDwtIGF2Z19maW5lc19kZlthdmdfZmluZXNfZGYkcHJlY2luY3QhPTIyLF0NCmBgYA0KDQojIyMzLiBQbG90IENob3JvcGxldGggTWFwcw0KDQpGaW5kIHRoZSBjZW50ZXJzIG9mIGVhY2ggcHJlY2luY3QgaW4gb3JkZXIgdG8gbGFiZWwgdGhlbSBpbiB0aGUgbWFwLg0KYGBge3J9DQpwcmVjaW5jdF9sYWJlbHMgPC0gbnlwcCAlPiUNCiAgZ3JvdXBfYnkocHJlY2luY3QpICU+JQ0KICBzdW1tYXJpc2UobGF0X2NlbnRlciA9IG1lYW4obGF0KSwgbG9uZ19jZW50ZXI9bWVhbihsb25nKSkNCmBgYA0KDQpHZXQgdGhlIHJhc3RlciBtYXAgYmFja2dyb3VuZC4NCmBgYHtyfQ0KTWFuaGF0dGFuX21hcCA8LSBnZXRfbWFwKCJNYW5oYXR0YW4iLCBzb3VyY2UgPSAic3RhbWVuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICB6b29tPTEyLCBtYXB0eXBlPSJ0ZXJyYWluLWJhY2tncm91bmQiKQ0KZyA8LSBnZ21hcChNYW5oYXR0YW5fbWFwKQ0KYGBgDQoNClBsb3QgYSBjaG9yb3BsZXRoIG1hcCBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHZpb2xhdGlvbnMgaW4gZWFjaCBwcmVjaW5jdC4NCmBgYHtyfQ0KZyArIA0KICBnZW9tX3BvbHlnb24oZGF0YSA9IHRvdGFsX3RpeF9kZiwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cD1ncm91cCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9dG90YWxfdGl4X2RmJG5fdmlvbGF0aW9ucyksIA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChuYW1lPSJOdW1iZXIgb2YgVG90YWwgVmlvbGF0aW9ucyIsDQogICAgICAgICAgICAgICAgICAgICAgbG93ID0gIiNmZmY3ZjciLGhpZ2ggPSAiI2ZmMDAwMCIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBwcmVjaW5jdF9sYWJlbHMsIGluaGVyaXQuYWVzID0gRkFMU0UsDQogICAgICAgICAgICBhZXMobGFiZWw9cHJlY2luY3QsIHggPSBsb25nX2NlbnRlciwgeSA9IGxhdF9jZW50ZXIpKQ0KYGBgDQoNClBsb3QgYSBjaG9yb3BsZXRoIG1hcCBvZiB0aGUgdG90YWwgYW1vdW50IG9mIGZpbmVzIGluIGVhY2ggcHJlY2luY3QuDQpgYGB7cn0NCmcgKyANCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB0b3RhbF9maW5lc19kZiwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cD1ncm91cCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gdG90YWxfZmluZXNfZGYkdG90YWxfZmluZXMpLCANCiAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC44KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobmFtZT0iQW1vdW50IG9mIFRvdGFsIEZpbmVzIiwNCiAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiI2ZmZjdmNyIsaGlnaCA9ICIjZmYwMDAwIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHByZWNpbmN0X2xhYmVscywgaW5oZXJpdC5hZXMgPSBGQUxTRSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1wcmVjaW5jdCwgeCA9IGxvbmdfY2VudGVyLCB5ID0gbGF0X2NlbnRlcikpDQoNCmBgYA0KDQpQbG90IGEgY2hvcm9wbGV0aCBtYXAgb2YgdGhlIGF2ZXJhZ2UgYW1vdW50IG9mIGZpbmVzIGluIGVhY2ggcHJlY2luY3QuDQpgYGB7cn0NCmcgKyANCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSBhdmdfZmluZXNfZGYsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXA9Z3JvdXAsIA0KICAgICAgICAgICAgICAgICBmaWxsID0gYXZnX2ZpbmVzX2RmJGF2Z19maW5lKSwgDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuOCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KG5hbWU9IkFtb3VudCBvZiBBdmVyYWdlIEZpbmVzIiwNCiAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiI2ZmZjdmNyIsaGlnaCA9ICIjZmYwMDAwIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHByZWNpbmN0X2xhYmVscywgaW5oZXJpdC5hZXMgPSBGQUxTRSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1wcmVjaW5jdCwgeCA9IGxvbmdfY2VudGVyLCB5ID0gbGF0X2NlbnRlcikpDQoNCmBgYA0KRnJvbSB0aGVzZSB0aHJlZSBtYXBzLCBJIHNlZSB0aGF0IHRoZSBtYXBzIGZvciB0b3RhbCBudW1iZXIgb2YgdGlja2V0cyBhbmQgZm9yIHRvdGFsIHJldmVudWUgbG9va3MgcHJldHR5IHNpbWlsYXIuIEluIHRoZXNlIHR3byBtYXBzLCBwcmVjaW5jdCAxOSBzdGFuZHMgb3V0IGFzIHRoZSBvbmUgd2l0aCBib3RoIHRoZSBtb3N0IG51bWJlciBvZiB0aWNrZXRzIGFuZCB5aWVsZGluZyB0aGUgbW9zdCByZXZlbnVlLCBmb2xsb3dlZCBieSBwcmVjaW5jdHMgMTgsIDE0LCBhbmQgMTMuIEhvd2V2ZXIsIHByZWNpbmN0IDE5J3MgdG9wIHJhbmtpbmcgaW4gdGhlc2UgdHdvIG1hcHMgbWF5IGJlIGR1ZSB0byBpdHMgbGFyZ2UgYXJlYS4gQmVjYXVzZSB3aGVuIGxvb2tpbmcgYXQgdGhlIGF2ZXJhZ2UgYW1vdW50IG9mIGZpbmVzIHBlciB0aWNrZXQsIHByZWNpbmN0IDE5J3MgcmFua2luZyBkcm9wcyBkb3duIHNpZ25pZmljYW50bHkgYW5kIHByZWNpbmN0cyAxOCwgMTcsIGFuZCAxNCBoYXZlIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgZmluZXMuDQoNCg0KIyNiLiAgVHlwZXMgb2YgdmlvbGF0aW9ucw0KDQojIyMxLiBTdHJ1Y3R1cmUgRGF0YQ0KDQoqR3JvdXAgdGhlIGFsbW9zdCAxMDAgdHlwZXMgb2YgdGlja2V0IHZpb2xhdGlvbnMgaW50byBhIHNtYWxsZXIgc2V0IG9mIDQtNiBzdWJncm91cHMgKHdoZXJlIG90aGVyIHNob3VsZCBiZSB0aGUgcmVtYWluZGVyIG9mIHZpb2xhdGlvbnMgbm90IGluY2x1ZGVkIGluIG90aGVyIGdyb3VwcyB5b3UgZGVmaW5lZCkuICoNCmBgYHtyfQ0KI0hlcmUgYXJlIHRoZSBncm91cHMgb2YgdmlvbGF0aW9ucyBhbmQgdGhlaXIgdmlvbGF0aW9uIGNvZGVzOg0KZG9jdW1lbnRhdGlvbiAgICAgPC0gYygyOSwJODIsCTEsCTc0LAk4MywJNzIsCTcxLAk3NSwJMiwJNzMsCTcwKQ0KaW1wcm9wZXJseV9wYXJrZWQgPC0gYyg2MCwJNTksCTYyLAk0NiwJNDcsCTYxKQ0Kc3RhbmRfc3RvcCAgICAgICAgPC0gYyg4LAkyMiwJNjQsCTE4LAkxOSwJMzEsCTE0LAkxNywJMTYsCTI2LAkxMSwJMTMsCTg5LAkzMCwJMTApDQpwcm9oaWJpdGVkX3pvbmUgICA8LSBjKDQ4LCA0LCA1MCwgNDksIDQwLCA1MiwJNzgsCTI0LAkyMSwJMjMsCQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOTgsCTksIDc3LCA1NCwgNjcsIDUzLCA1MSwgNDUsIDMpDQp0aW1lX2xpbWl0ICAgICAgICA8LSBjKDM3LAk0MiwJMzgsCTg2LAkyMCwJMzksCTQ0LAk2OSkNCm90aGVyICAgICAgICAgICAgIDwtIGMoNjYsIDQxLAk4MCwJNjgsCTk5LAk4NCwJMzUsCTg1LAk3OSkNCg0KI0NyZWF0ZSBkdW1teSB2YXJpYWJsZXMgd2l0aCB0aGUgZGlmZmVyZW50IHR5cGVzIG9mIHZpb2xhdGlvbnMuDQp2aW9sdG5fdHlwZV9kZiA8LSBNYW5oYXR0YW5fdmlvbGF0aW9ucyAlPiUNCiAgbXV0YXRlKHZpb2x0bl9kb2N1bWVudGF0aW9uID0gY2FzZV93aGVuKA0KICAgIHZpb2xhdGlvbl9jb2RlICVpbiUgZG9jdW1lbnRhdGlvbiB+IDEsDQogICAgVFJVRSB+IDAgKSkgJT4lDQogIG11dGF0ZSh2aW9sdG5faW1wcm9wZXJseV9wYXJrZWQgPSBjYXNlX3doZW4oDQogICAgdmlvbGF0aW9uX2NvZGUgJWluJSBpbXByb3Blcmx5X3BhcmtlZCB+IDEsDQogICAgVFJVRSB+IDAgKSkgJT4lDQogIG11dGF0ZSh2aW9sdG5fc3RhbmRfc3RvcCA9IGNhc2Vfd2hlbigNCiAgICB2aW9sYXRpb25fY29kZSAlaW4lIHN0YW5kX3N0b3AgfiAxLA0KICAgIFRSVUUgfiAwICkpICU+JQ0KICBtdXRhdGUodmlvbHRuX3Byb2hpYml0ZWRfem9uZSA9IGNhc2Vfd2hlbigNCiAgICB2aW9sYXRpb25fY29kZSAlaW4lIHByb2hpYml0ZWRfem9uZSB+IDEsDQogICAgVFJVRSB+IDAgKSkgJT4lDQogIG11dGF0ZSh2aW9sdG5fdGltZV9saW1pdCA9IGNhc2Vfd2hlbigNCiAgICB2aW9sYXRpb25fY29kZSAlaW4lIHRpbWVfbGltaXQgfiAxLA0KICAgIFRSVUUgfiAwICkpICU+JQ0KICBtdXRhdGUodmlvbHRuX290aGVyID0gY2FzZV93aGVuKA0KICAgIHZpb2xhdGlvbl9jb2RlICVpbiUgb3RoZXIgfiAxLA0KICAgIFRSVUUgfiAwICkpDQpgYGANCg0KQ291bnQgdmlvbGF0aW9uIHR5cGVzIGJ5IHByZWNpbmN0DQpgYGB7cn0NCnZpb2x0bl90eXBlX2RmIDwtIHZpb2x0bl90eXBlX2RmICU+JQ0KICBncm91cF9ieSh2aW9sYXRpb25fcHJlY2luY3QpICU+JQ0KICBzdW1tYXJpc2UodmlvbHRuX2RvY3VtZW50YXRpb249IHN1bSh2aW9sdG5fZG9jdW1lbnRhdGlvbiksIA0KICAgICAgICAgICAgdmlvbHRuX2ltcHJvcGVybHlfcGFya2VkPSBzdW0odmlvbHRuX2ltcHJvcGVybHlfcGFya2VkKSwgDQogICAgICAgICAgICB2aW9sdG5fcHJvaGliaXRlZF96b25lPSBzdW0odmlvbHRuX3Byb2hpYml0ZWRfem9uZSksIA0KICAgICAgICAgICAgdmlvbHRuX3N0YW5kX3N0b3A9IHN1bSh2aW9sdG5fc3RhbmRfc3RvcCksDQogICAgICAgICAgICB2aW9sdG5fdGltZV9saW1pdD1zdW0odmlvbHRuX3RpbWVfbGltaXQpLA0KICAgICAgICAgICAgdmlvbHRuX290aGVyPXN1bSh2aW9sdG5fb3RoZXIpKQ0KYGBgDQoNCiMjIzIgSm9pbiBEYXRhc2V0cw0KSm9pbiB0aGUgbnlwcCBhbmQgdmlvbGF0aW9ucyBieSB0eXBlIGRhdGFzZXRzDQpgYGB7cn0NCnZpb2x0bl90eXBlX2RmIDwtIGxlZnRfam9pbihueXBwLCB2aW9sdG5fdHlwZV9kZiwgYnkgPSBjKCJwcmVjaW5jdCIgPSAidmlvbGF0aW9uX3ByZWNpbmN0IikpDQoNCiNkcm9wIHByZWNpbmN0IDIyIGRhdGEsIHNpbmNlIHRoZXJlIGFyZSBubyB2aW9sYXRpb25zIGluIHRoaXMgcHJlY2luY3QuDQp2aW9sdG5fdHlwZV9kZiA8LSB2aW9sdG5fdHlwZV9kZlt2aW9sdG5fdHlwZV9kZiRwcmVjaW5jdCE9MjIsXQ0KYGBgDQoNCiMjIzMgUGxvdCBDaG9yb3BsZXRoIE1hcHMNCg0KKlByb3ZpZGUgY2hvcm9wbGV0aCBtYXBzIGZvciBlYWNoIG9mIHRoZXNlIHN1Ymdyb3VwcyB0byBzaG93IHdoZXJlIGRpZmZlcmVudCB0eXBlcyBvZiB2aW9sYXRpb25zIGFyZSBtb3JlIG9yIGxlc3MgY29tbW9uLioNCg0KKipWZWhpY2xlcyB3aXRoIEltcHJvcGVyIERvY3VtZW50YXRpb24qKg0KYGBge3J9DQpnICsgDQogIGdlb21fcG9seWdvbihkYXRhID0gdmlvbHRuX3R5cGVfZGYsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXA9Z3JvdXAsIA0KICAgICAgICAgICAgICAgICBmaWxsID0gdmlvbHRuX2RvY3VtZW50YXRpb24pLCANCiAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC44KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobmFtZT0iTnVtYmVyIG9mXG5kb2N1bWVudGF0aW9uXG52aW9sYXRpb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiI2ZmZjdmNyIsaGlnaCA9ICIjZmYwMDAwIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHByZWNpbmN0X2xhYmVscywgaW5oZXJpdC5hZXMgPSBGQUxTRSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1wcmVjaW5jdCwgeCA9IGxvbmdfY2VudGVyLCB5ID0gbGF0X2NlbnRlcikpICsNCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueT1lbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KKipJbXByb3Blcmx5IFBhcmtlZCBWZWhpY2xlcyoqDQpgYGB7cn0NCmcgKyANCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB2aW9sdG5fdHlwZV9kZiwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cD1ncm91cCwgDQogICAgICAgICAgICAgICAgIGZpbGwgPSB2aW9sdG5faW1wcm9wZXJseV9wYXJrZWQpLCANCiAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC44KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobmFtZT0iTnVtYmVyIG9mXG5pbXByb3BlciBwYXJraW5nXG52aW9sYXRpb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiI2ZmZjdmNyIsaGlnaCA9ICIjZmYwMDAwIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHByZWNpbmN0X2xhYmVscywgaW5oZXJpdC5hZXMgPSBGQUxTRSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1wcmVjaW5jdCwgeCA9IGxvbmdfY2VudGVyLCB5ID0gbGF0X2NlbnRlcikpICsNCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueT1lbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KKipWZWhpY2xlcyBQYXJrZWQgaW4gUHJvaGliaXRlZCBab25lcyoqDQpgYGB7cn0NCmcgKyANCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB2aW9sdG5fdHlwZV9kZiwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cD1ncm91cCwgDQogICAgICAgICAgICAgICAgIGZpbGwgPSB2aW9sdG5fcHJvaGliaXRlZF96b25lKSwgDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuOCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KG5hbWU9Ik51bWJlciBvZlxudmVoaWNsZXMgcGFya2VkIGluXG5wcm9oaWJpdGVkIHpvbmVzIiwNCiAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiI2ZmZjdmNyIsaGlnaCA9ICIjZmYwMDAwIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHByZWNpbmN0X2xhYmVscywgaW5oZXJpdC5hZXMgPSBGQUxTRSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1wcmVjaW5jdCwgeCA9IGxvbmdfY2VudGVyLCB5ID0gbGF0X2NlbnRlcikpICsNCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueT1lbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KKipTdGFuZGluZyBhbmQgU3RvcHBpbmcgVmlvbGF0aW9ucyoqDQpgYGB7cn0NCmcgKyANCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB2aW9sdG5fdHlwZV9kZiwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cD1ncm91cCwgDQogICAgICAgICAgICAgICAgIGZpbGwgPSB2aW9sdG5fc3RhbmRfc3RvcCksIA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChuYW1lPSJOdW1iZXIgb2ZcbnN0YW5kaW5nIGFuZCBzdG9wcGluZ1xudmlvbGF0aW9ucyIsDQogICAgICAgICAgICAgICAgICAgICAgbG93ID0gIiNmZmY3ZjciLGhpZ2ggPSAiI2ZmMDAwMCIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBwcmVjaW5jdF9sYWJlbHMsIGluaGVyaXQuYWVzID0gRkFMU0UsDQogICAgICAgICAgICBhZXMobGFiZWw9cHJlY2luY3QsIHggPSBsb25nX2NlbnRlciwgeSA9IGxhdF9jZW50ZXIpKSArDQogIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnk9ZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCioqVGltZSBMaW1pdCBWaW9sYXRpb25zKioNCmBgYHtyfQ0KZyArIA0KICBnZW9tX3BvbHlnb24oZGF0YSA9IHZpb2x0bl90eXBlX2RmLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwPWdyb3VwLCANCiAgICAgICAgICAgICAgICAgZmlsbCA9IHZpb2x0bl90aW1lX2xpbWl0KSwgDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuOCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KG5hbWU9Ik51bWJlciBvZlxudGltZSBsaW1pdFxudmlvbGF0aW9ucyIsDQogICAgICAgICAgICAgICAgICAgICAgbG93ID0gIiNmZmY3ZjciLGhpZ2ggPSAiI2ZmMDAwMCIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBwcmVjaW5jdF9sYWJlbHMsIGluaGVyaXQuYWVzID0gRkFMU0UsDQogICAgICAgICAgICBhZXMobGFiZWw9cHJlY2luY3QsIHggPSBsb25nX2NlbnRlciwgeSA9IGxhdF9jZW50ZXIpKSArDQogIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnk9ZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCioqT3RoZXIgVmlvbGF0aW9ucyoqDQpgYGB7cn0NCmcgKyANCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB2aW9sdG5fdHlwZV9kZiwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cD1ncm91cCwgDQogICAgICAgICAgICAgICAgIGZpbGwgPSB2aW9sdG5fb3RoZXIpLCANCiAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC44KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobmFtZT0iTnVtYmVyIG9mXG51bmNhdGVnb3JpemVkXG52aW9sYXRpb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiI2ZmZjdmNyIsaGlnaCA9ICIjZmYwMDAwIikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHByZWNpbmN0X2xhYmVscywgaW5oZXJpdC5hZXMgPSBGQUxTRSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1wcmVjaW5jdCwgeCA9IGxvbmdfY2VudGVyLCB5ID0gbGF0X2NlbnRlcikpICsNCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueT1lbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KDQojMy4gRm9jdXMgb24gdGhlIFVwcGVyIEVhc3QgU2lkZQ0KIyNhLiBJZ25vcmluZyBmaXJlIGh5ZHJhbnRzDQoNCipSZXN0cmljdCBkYXRhIHRvIHBhcmtpbmcgdmlvbGF0aW9ucyByZWxhdGVkIHRvIGZpcmUgaHlkcmFudHMuKg0KDQpBbHNvIHJlc3RyaWN0IHRvIHZpb2xhdGlvbiBhZGRyZXNzZXMgd2l0aCBudW1iZXJzIGFzIHRoZSBob3VzZSBudW1iZXIsIHRvIGF2b2lkIGlzc3VlcyB3aXRoIGdlb2NvZGluZyBsYXRlciBvbiwgYW5kIHZlaGljbGVzIHdpdGggcGFzc2VuZ2VyIHBsYXRlcywgdG8gcmVkdWNlIHRoZSBhbW91bnQgb2YgZGF0YS4NCmBgYHtyfQ0KTWFuaGF0dGFuX2RmX1EzIDwtIE1hbmhhdHRhbl92aW9sYXRpb25zDQpNYW5oYXR0YW5fZGZfUTMkaG91c2VfbnVtYmVyIDwtIGFzLm51bWVyaWMoTWFuaGF0dGFuX2RmX1EzJGhvdXNlX251bWJlcikNClVFU19kZiA8LSBNYW5oYXR0YW5fZGZfUTMgJT4lDQogIHN1YnNldCh2aW9sYXRpb25fcHJlY2luY3QgPT0gMTkgJiB2aW9sYXRpb25fY29kZSA9PSA0MCAmIA0KICAgICAgICAgICAhaXMubmEoaG91c2VfbnVtYmVyKSAmIHBsYXRlX3R5cGUgPT0gIlBBUyIpDQpgYGANCg0KS2VlcCBvbmx5IHVzZWZ1bCB2YXJpYWJsZXMgaW4gdGhlIFVFUyBkYXRhDQpgYGB7cn0NClVFU19kZiA8LSBjYmluZChVRVNfZGZbMToyXSwgVUVTX2RmWzhdLCBVRVNfZGZbMjQ6MjVdLCBVRVNfZGZbNDZdKQ0KYGBgDQoNCipVc2luZyB0aGUgdmFyaWFibGVzICJTdHJlZXQgTmFtZSIgYW5kICJIb3VzZSBOdW1iZXIiIGFzIHdlbGwgYXMgdGhlIGtub3dsZWRnZSB0aGF0IHRoZXNlIGFkZHJlc3NlcyBhcmUgaW4gdGhlIFVwcGVyIEVhc3QgU2lkZSBvZiBNYW5oYXR0YW4sIGdlb2NvZGUgYXQgbGVhc3QgNTAwIGFkZHJlc3Nlcy4qDQpgYGB7cn0NCiMgbWFrZSBhIG5ldyB2YXJpYWJsZSAiYWRkcmVzcyINClVFU19kZiRhZGRyZXNzIDwtIHBhc3RlKFVFU19kZiRob3VzZV9udW1iZXIsIFVFU19kZiRzdHJlZXRfbmFtZSwgIiwgdXBwZXIgZWFzdCBzaWRlLCBNYW5oYXR0YW4gTlkiKQ0KZ2VvY29kZXNfZGYgPC0gcmVhZF9jc3YoImdlb2NvZGVzX2RmLmNzdiIpDQojZ2VvY29kZXNfZGYgPC0gZ2VvY29kZShVRVNfZGYkYWRkcmVzcykNCmBgYA0KDQphZGQgZ2VvY29kZXMgdG8gdGhlIFVFUyBkYXRhDQpgYGB7cn0NClVFU19kZiA8LSBjYmluZChVRVNfZGZbMTozXSwgVUVTX2RmWzY6N10sIGdlb2NvZGVzX2RmKQ0KYGBgDQoNCipJbmNsdWRlIGEgZGF0YSB0YWJsZSBvZiB0aGVzZSBhZGRyZXNzZXMgYW5kIHRoZSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIG9mIHRoZXNlIGFkZHJlc3NlcyBpbiB0aGUgb3V0cHV0LioNCmBgYHtyfQ0KbGlicmFyeShEVCkNCg0KZHRfaGVhZGVycyA8LSBjKCJTdW1tb25zIElEIiwgIlBsYXRlIElEIiwgIk1ha2UiLCAiVmlvbGF0aW9uIFR5cGUiLCAiQWRkcmVzcyBvZiBWaW9sYXRpb24iLCAiTG9uZ2l0dWRlIiwgIkxhdGl0dWRlIikNCg0KZGF0YXRhYmxlKFVFU19kZiwgcm93bmFtZXM9RkFMU0UsIGNvbG5hbWVzPWR0X2hlYWRlcnMsDQogICAgICAgICAgZmlsdGVyPWxpc3QocG9zaXRpb249InRvcCIpLCBvcHRpb25zID0gbGlzdChjb2x1bW5EZWZzID0gbGlzdChsaXN0KGNsYXNzTmFtZSA9ICdkdC1sZWZ0JywgdGFyZ2V0cyA9IDA6MikpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKQ0KYGBgDQoNCiMjYi4gSW50ZXJhY3RpdmUgTWFwDQoNCmBgYHtyfQ0KbGlicmFyeShsZWFmbGV0KQ0KDQpsaWJyYXJ5KHJlYWRyKQ0KYGBgDQoNClN1YnNldCBkYXRhIHNvIHRoYXQgaXQgZXhjbHVkZXMgYW55IHZpb2xhdGlvbnMgZ2VvY29kZWQgdG8gc2hvdyB1cCBvdXRzaWRlIG9mIHRoZSB1cHBlciBlYXN0IHNpZGUgKGxpa2VseSBpbmNvcnJlY3RseSBjb2RlZCB0byBwcmVjaW5jdCAxOSkuDQpgYGB7cn0NClVFU19kZiA8LSBVRVNfZGZbVUVTX2RmJGxvbiA+IC03My45NzIgJiBVRVNfZGYkbG9uIDwgLTczLjk0NSAmIFVFU19kZiRsYXQgPiA0MC43NjAgJiBVRVNfZGYkbGF0IDwgNDAuNzg4LCBdDQoNCg0KYGBgDQoNCkNyZWF0ZSBwb3B1cCBjb250ZW50Lg0KYGBge3J9DQpjb250ZW50IDwtIHBhc3RlKCJQbGF0ZSBJRDoiLCBVRVNfZGYkcGxhdGVfaWQsICI8YnIvPiIsDQogICAgICAgICAgICAgICAgICJWZWhpY2xlIE1ha2U6IiwgVUVTX2RmJHZlaGljbGVfbWFrZSwgIjxici8+IiwNCiAgICAgICAgICAgICAgICAgIkFkZHJlc3M6IiwgVUVTX2RmJGFkZHJlc3MpDQpgYGANCg0KUHJvdmlkZSBhbiBpbnRlcmFjdGl2ZSBtYXAgb2YgdGhlIHZpb2xhdGlvbnMgeW91IGdlb2NvZGVkIHVzaW5nIGxlYWZsZXQuIFByb3ZpZGUgYXQgbGVhc3QgdGhyZWUgcGllY2VzIG9mIGluZm9ybWF0aW9uIG9uIHRoZSBwYXJraW5nIHRpY2tldCBpbiBhIHBvcHVwLg0KDQoqKkknbSBub3Qgc3VyZSB3aHkgdGhlIGFkZFRpbGVzKCkgYW5kIGFkZFByb3ZpZGVyVGlsZXMoKSBhcmUgbm90IHdvcmtpbmcuIEl0IGFwcGVhcnMgdGhhdCB0aGUgY29kZSBpcyBjb3JyZWN0IGJ1dCB0aGVyZSBpcyBhbiBpc3N1ZSBwZXJoYXBzIHdpdGggbXkgY29kaW5nIGVudmlyb25tZW50IG9yIHNldCB1cC4gRWRkaWUgYW5kIEkgaGF2ZSBhIGNvbnZlcnNhdGlvbiBhYm91dCB0aGlzIG9uIFBpYXp6YSBidXQgdGhpcyBpc3N1ZSB3YXMgbm90IHJlc29sdmVkIGJ5IHRoZSB0aW1lIHRoZSBhc3NpZ25tZW50IHdhcyBzdWJtaXR0ZWQuIFRoZXJlZm9yZSwgYWxsIG9mIG15IGludGVyYWN0aXZlIG1hcHMgaW4gcXVlc3Rpb24gMyBkbyBub3QgaGF2ZSB0aWxlcyBhZGRlZC4qKg0KYGBge3J9DQptYXAzYiA8LSBsZWFmbGV0KFVFU19kZiwgb3B0aW9ucyA9IGxlYWZsZXRPcHRpb25zKG1pblpvb20gPSAxMikpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKCJTdGFtZW4uVG9uZXJMaXRlIikgJT4lICAgDQogIGFkZENpcmNsZXMoY29sPSAieWVsbG93Iiwgb3BhY2l0eT0xLCBwb3B1cCA9IGNvbnRlbnQsDQogICAgICAgICAgICAgaGlnaGxpZ2h0T3B0aW9ucyA9IGhpZ2hsaWdodE9wdGlvbnMoDQogICAgICAgICAgICAgICBjb2xvcj0nIzAwNjFmZicsIHdlaWdodCA9IDUsDQogICAgICAgICAgICAgICBicmluZ1RvRnJvbnQgPSBUUlVFLCBzZW5kVG9CYWNrID0gVFJVRSkpICU+JQ0KICBzZXRWaWV3KGxuZyA9IC03My45NTYsIGxhdCA9IDQwLjc3MzgsIHpvb20gPSAxNCkNCiAgDQptYXAzYg0KYGBgDQoNCiMjYy4gTHV4dXJ5IENhcnMgYW5kIFJlcGVhdCBPZmZlbmRlcnMNClVzaW5nIHRoZSB2ZWhpY2xlIFBsYXRlIElELCBpZGVudGlmeSByZXBlYXQgb2ZmZW5kZXJzIChpbiB0aGUgZnVsbCBkYXRhc2V0KS4NCmBgYHtyfQ0KcmVwZWF0ZXJzX2RmIDwtIE1hbmhhdHRhbl92aW9sYXRpb25zICU+JQ0KICBncm91cF9ieShwbGF0ZV9pZCkgJT4lDQogIHN1bW1hcmlzZShudW1fb2ZmZW5zZXMgPSBuKCkpDQpgYGANCg0KQ3JlYXRlIGFub3RoZXIgdmFyaWFibGUgY2FsbGVkIGx1eHVyeV9jYXIgaW4gd2hpY2ggeW91IGlkZW50aWZ5IGx1eHVyeSBjYXIgYnJhbmRzIHVzaW5nIHRoZSBWZWhpY2xlIE1ha2UgdmFyaWFibGUuDQpgYGB7cn0NCmx1eHVyeV9icmFuZHMgPC0gYygiQk1XIiwJIkZJQVQiLAkiQ0hSWVMiLAkiTEVYVVMiLAkiQ0FESUwiLAkiQVVESSIsCSJQT1JTQyIsCQ0KICAgICAgICAgICAgICAgICAgICJKQUdVQSIsCSJDQURJIiwJIkNIUlkiLAkiTElOQyIsCSJGRVJSQSIsCSJMQU1CTyIsCSJMRVhVIiwJDQogICAgICAgICAgICAgICAgICAgIlpFTklUIiwJIlpFTlRJIiwJIlJPTExTIiwJIk1BU0UiLAkiQkVOVEwiLAkiTElOQ08iKQ0KDQpsdXh1cnlfY2Fyc19kZiA8LSBNYW5oYXR0YW5fdmlvbGF0aW9ucyAlPiUNCiAgbXV0YXRlKGNhcl90eXBlID0gY2FzZV93aGVuKA0KICAgIHZlaGljbGVfbWFrZSAlaW4lIGx1eHVyeV9icmFuZHMgfiAiTHV4dXJ5IENhciIsDQogICAgVFJVRSB+ICJOb24tTHV4dXJ5IENhciINCiAgKSkNCmBgYA0KDQoNCk1lcmdlIGx1eHVyeV9jYXJzX2RmIHdpdGggZ2VvY29kZXMgYW5kIHRoZW4gd2l0aCByZXBlYXRlcnNfZGYsIHVzaW5nIHBsYXRlX2lkDQpgYGB7cn0NCmx1eHVyeV9jYXJzX2RmIDwtIGx1eHVyeV9jYXJzX2RmICU+JQ0KICBsZWZ0X2pvaW4ocmVwZWF0ZXJzX2RmLCBieSA9ICJwbGF0ZV9pZCIpICU+JQ0KICBtdXRhdGUocmVwZWF0ZXIgPSBjYXNlX3doZW4oICAgICAgICAgICAgICAgI21ha2UgYmluYXJ5IHZhcmlhYmxlIHRvIGlkZW50aWZ5IHJlcGVhdGVycw0KICAgIG51bV9vZmZlbnNlcyA+IDEgfiAiWWVzIiwNCiAgICBUUlVFIH4gIk5vIg0KICApKQ0KYGBgDQoNCktlZXAgb25seSBuZWNlc3NhcnkgdmFyaWFibGVzLCB0aGVuIGpvaW4gd2l0aCBVRVNfZGYNCmBgYHtyfQ0KbHV4dXJ5X2NhcnNfZGYgPC0gbHV4dXJ5X2NhcnNfZGYgJT4lDQogIHNlbGVjdChzdW1tb25zX251bWJlciwgY2FyX3R5cGUsIHJlcGVhdGVyKQ0KDQpVRVNfZGZfM2MgPC0gVUVTX2RmICU+JQ0KICBsZWZ0X2pvaW4obHV4dXJ5X2NhcnNfZGYsIGJ5ID0gInN1bW1vbnNfbnVtYmVyIikNCmBgYA0KDQpzZXQgdGhlIGNvbG9yIHNjaGVtZSBmb3IgY2FyIHR5cGUNCmBgYHtyfQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpwYWxldHRlXzNiID0gY29sb3JGYWN0b3IoIlNldDEiLCBkb21haW4gPSBVRVNfZGZfM2MkY2FyX3R5cGUpICMgR3JhYiBhIHBhbGV0dGUNCmNvbG9yX2Nhcl90eXBlID0gcGFsZXR0ZV8zYihVRVNfZGZfM2MkY2FyX3R5cGUpDQpgYGANCg0KY3JlYXRlIG5ldyBwb3B1cCBjb250ZW50DQpgYGB7cn0NCmNvbnRlbnQyIDwtIHBhc3RlKCJQbGF0ZSBJRDoiLCBVRVNfZGZfM2MkcGxhdGVfaWQsICI8YnIvPiIsDQogICAgICAgICAgICAgICAgICJWZWhpY2xlIE1ha2U6IiwgVUVTX2RmXzNjJHZlaGljbGVfbWFrZSwgIjxici8+IiwNCiAgICAgICAgICAgICAgICAgIkFkZHJlc3M6IiwgVUVTX2RmXzNjJGFkZHJlc3MsICI8YnIvPiIsDQogICAgICAgICAgICAgICAgICJDYXIgVHlwZToiLCBVRVNfZGZfM2MkY2FyX3R5cGUsICI8YnIvPiIsDQogICAgICAgICAgICAgICAgICJSZXBlYXQgT2ZmZW5kZXI6IiwgVUVTX2RmXzNjJHJlcGVhdGVyKQ0KYGBgDQoNClN0YXJ0IHdpdGggdGhlIHByZXZpb3VzIG1hcC4gRGlzdGluZ3Vpc2ggdGhlIHBvaW50cyBieSB3aGV0aGVyIHRoZSBjYXIgaXMgYSByZXBlYXQgb2ZmZW5kZXIgYW5kL29yIGx1eHVyeSBjYXIuIEFkZCBhIGxlZ2VuZCBpbmZvcm1pbmcgdGhlIHVzZXIgYWJvdXQgdGhlIGNvbG9yIHNjaGVtZS4gQWxzbyBtYWtlIHN1cmUgdGhhdCB0aGUgYWRkZWQgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGNhciB0eXBlIGFuZCByZXBlYXQgb2ZmZW5kZXIgc3RhdHVzIGlzIG5vdyBjb250YWluZWQgaW4gdGhlIHBvcHVwIGluZm9ybWF0aW9uLiBTaG93IHRoaXMgbWFwLg0KYGBge3J9DQojYWRkIG9uIHRvIHRoZSBvcmlnaW5hbCBtYXAgZnJvbSAzYi4NCm1hcDNjIDwtIGxlYWZsZXQoVUVTX2RmXzNjLCBvcHRpb25zID0gbGVhZmxldE9wdGlvbnMobWluWm9vbSA9IDEyKSkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMoIlN0YW1lbi5Ub25lckxpdGUiKSAlPiUgICANCiAgYWRkQ2lyY2xlcyhjb2xvciA9IGNvbG9yX2Nhcl90eXBlLCBvcGFjaXR5PTEsIHBvcHVwID0gY29udGVudDIsDQogICAgICAgICAgICAgaGlnaGxpZ2h0T3B0aW9ucyA9IGhpZ2hsaWdodE9wdGlvbnMoDQogICAgICAgICAgICAgICBjb2xvcj0nIzAwNjFmZicsIHdlaWdodCA9IDUsDQogICAgICAgICAgICAgICBicmluZ1RvRnJvbnQgPSBUUlVFLCBzZW5kVG9CYWNrID0gVFJVRSkpICU+JQ0KICBhZGRMZWdlbmQocGFsID0gcGFsZXR0ZV8zYiwgdmFsdWVzID0gflVFU19kZl8zYyRjYXJfdHlwZSwgdGl0bGUgPSAiQ2FyIFR5cGUiKSAlPiUNCiAgc2V0VmlldyhsbmcgPSAtNzMuOTU2LCBsYXQgPSA0MC43NzM4LCB6b29tID0gMTQpDQptYXAzYw0KYGBgDQoNCg0KIyNkLiBDbHVzdGVyDQpBZGQgbWFya2VyIGNsdXN0ZXJpbmcsIHNvIHRoYXQgem9vbWluZyBpbiB3aWxsIHJldmVhbCB0aGUgaW5kaXZpZHVhbCBsb2NhdGlvbnMgYnV0IHRoZSB6b29tZWQgb3V0IG1hcCBvbmx5IHNob3dzIHRoZSBjbHVzdGVycy4gU2hvdyB0aGUgbWFwIHdpdGggY2x1c3RlcnMuDQpgYGB7cn0NCg0KbWFwM2QgPC0gbGVhZmxldChVRVNfZGZfM2MsIG9wdGlvbnMgPSBsZWFmbGV0T3B0aW9ucyhtaW5ab29tID0gMTIpKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcygiU3RhbWVuLlRvbmVyTGl0ZSIpICU+JSAgIA0KICBhZGRDaXJjbGVNYXJrZXJzKGNvbG9yID0gY29sb3JfY2FyX3R5cGUsIA0KICAgICAgICAgICAgICAgICAgICAgICBwb3B1cCA9IGNvbnRlbnQyLA0KICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyT3B0aW9ucyA9IG1hcmtlckNsdXN0ZXJPcHRpb25zKCkpICU+JQ0KICBhZGRMZWdlbmQocGFsID0gcGFsZXR0ZV8zYiwgdmFsdWVzID0gflVFU19kZl8zYyRjYXJfdHlwZSwgdGl0bGUgPSAiQ2FyIFR5cGUiKSAlPiUNCiAgc2V0VmlldyhsbmcgPSAtNzMuOTU2LCBsYXQgPSA0MC43NzM4LCB6b29tID0gMTQpIA0KbWFwM2QNCmBgYA0KDQoNCg0K